#include "audioplayer.h"

void audio_callback(void *userdata, Uint8 *stream, int len) {
    int len1;
    int audio_size;
    double pts;
    AudioPlayer* ap = (AudioPlayer*)userdata;

    while(len > 0) {
        if (ap->getState() == PLAY || ap->getState() == STOPPING) {
            if(ap->buf_index >= ap->buf_size) {
            /* We have already sent all our data; get more */
                audio_size = ap->audio_decode_frame(ap->buffer, sizeof(ap->buffer), &pts);
                if(audio_size < 0) {
                    /* If error, output silence */
                    ap->buf_size = 1024;
                    memset(ap->buffer, 0, ap->buf_size);
                } else {
                    ap->buf_size = audio_size;
                }
                ap->buf_index = 0;
            }
            len1 = ap->buf_size - ap->buf_index;
            if(len1 > len)
                len1 = len;
            SDL_MixAudio(stream, (uint8_t *)ap->buffer + ap->buf_index, len1, ap->getVolume());
            //memcpy(stream, (uint8_t *)ap->buffer + ap->buf_index, len1);
            len -= len1;
            stream += len1;
            ap->buf_index += len1;
        }
        else {
            ap->buf_size = 1024;
            memset(ap->buffer, 0, ap->buf_size);
            break;
        }
    }
}

AudioPlayer::AudioPlayer(QObject *parent, Video* v) :
    QObject(parent), v(v)
{
    SDL_AudioSpec wanted_spec;
    SDL_AudioSpec spec;
    wanted_spec.freq = v->aCodecCtx->sample_rate;
    wanted_spec.format = AUDIO_S16SYS;
    wanted_spec.channels = v->aCodecCtx->channels; //v->aCodecCtx->channels;
    wanted_spec.silence = 0;
    wanted_spec.samples = 1024;
    wanted_spec.callback = audio_callback;
    wanted_spec.userdata = this;

    if(SDL_OpenAudio(&wanted_spec, &spec) < 0) {
        std::cout << "SDL_OpenAudio: " << SDL_GetError() << std::endl;
    }
    else if (spec.format != AUDIO_S16SYS || spec.channels != v->aCodecCtx->channels) {
        std::cout << "Audio format unsupported" << std::endl;
    }
    else
        SDL_PauseAudio(0);

    buf_index = 0;
    buf_size = 0;
    curpkt.size = 0;
}

AudioPlayer::~AudioPlayer() {
    SDL_PauseAudio(1);
    SDL_CloseAudio();
    std::cout << "audio player finished" << std::endl;
}

int AudioPlayer::audio_decode_frame(uint8_t *audio_buf, int buf_size, double *pts_ptr) {

    int gotframe;
    int len;
    //if stopped or paused. dont consume frame
    if (v->state == STOPPED || v->state == PAUSED) {
        return -1;
    }

    AVFrame *frame = avcodec_alloc_frame();

    while(true) {
        while (curpkt.size > 0) {
            len = avcodec_decode_audio4(v->aCodecCtx, frame, &gotframe, &curpkt);
            if (len < 0) { // error occured decoding the packet. skip packet
                av_free_packet(&curpkt);
                break;
            }
            curpkt.data+=len;
            curpkt.size-=len;
            if (len == 0) //no data yet. keep reading
                continue;
            if(gotframe) {
                //got data, copy the data of the first channel to our audio buffer
                int size = av_samples_get_buffer_size(NULL,v->aCodecCtx->channels,
                           frame->nb_samples, v->aCodecCtx->sample_fmt, 1);
                memcpy(audio_buf, frame->extended_data[0], size);
                //return the amount of bytes read into the buffer
                return size;
            }
            else {
                continue;
            }
        }
        //the packet was consumed.
        //if (!curpkt.size)
        //    av_free_packet(&curpkt);

        //grab next packet, if there is no audio packet in queue, play silence
        if(v->aPacketQueue->size > 0) {
            //make sure that the audio packet comes later than the videoclock
            if (v->aPacketQueue->pop(&curpkt) < 0)
                break;
            else if (curpkt.pts < v->videoClock + v->vidTimeElapsed) {
                continue;
            }

            pkt_size = curpkt.size;
        }
        else {
            //audio callback function will fill buffer with 0.
            break;
        }
    }

    av_free(frame);
    return -1;
}

unsigned int AudioPlayer::getVolume() {
    return v->volume;
}

VideoState AudioPlayer::getState() {
    return v->state;
}

